/*****************************************************************************************
   
   Copyright (c) Microsoft Corporation. All rights reserved.

   Use of this code sample is subject to the terms of the Microsoft
   Permissive License, a copy of which should always be distributed with
   this file.  You can also access a copy of this license agreement at:
   http://www.microsoft.com/resources/sharedsource/licensingbasics/permissivelicense.mspx

 ****************************************************************************************/

using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using System.Xml;

using GrooveWebServicesV12Helpers;
using GrooveWebServicesV12Helpers.InfoPathForms;

namespace GrooveInfoPathForms
{
    class GrooveInfoPathForms : CommandUtils.GrooveUtilBase
    {
        Boolean mImport;
        Boolean mExport;
        Boolean mTemplate;
        Boolean mExportAnyRecord;

        String mFormURI;
        String mRecordURI;
        String mPath;
        String mExamplePath;

        [STAThread]
        static void Main(string[] i_Arguments)
        {
            CommandUtils.UtilAppMain.RunApplication(new GrooveInfoPathForms(i_Arguments));
        }

        public GrooveInfoPathForms(string[] i_Arguments)
            : base(i_Arguments)
        {
            base.UsageMessage = "GrooveInfoPathForms [PARAMS...]";
            base.HelpMessage =
                "GrooveInfoPathForms\r\n\t" +
                "Imports and Exports InfoPath Forms and InfoPath Form Templates\r\n" + 
                "Parameters:\r\n\t" +
                "\r\n\t" + 
                "[/Import             Import an InfoPath form instance to a InfoPath Forms tool form\r\n\t" +     
                "    /FormURI=         URI of the form to import the instance to\r\n\t" +
                "    /Path=            FilePath of the form xml\r\n\t" +
                "    /ExamplePath=     FilePath of the Example form xml (e.g., exported from this FormURI as an example)]\r\n\t" +
                "\r\n\t" +
                "[/Export             Export an InfoPath form instance to a file\r\n\t" +
                "    /RecordURI=       URI of the record to export (if ExportAnyRecord is NOT set)\r\n\t" +
                "    /FormURI=         URI of the form to export some record from (if ExportAnyRecord is set)\r\n\t" +
                "    /ExportAnyRecord  Set this flag to export any record from the form, for use as an import example\r\n\t" +
                "    /Path=            FilePath to export the form instance to as xml\r\n\t" +
                "    /ExamplePath=     FilePath of the Example form xml]\r\n\t" +
                "\r\n\t" +
                "[/Template            mode is export template - export a form template\r\n\t" +
                "    /FormURI=         URI of the form to export the template for\r\n\t" +
                "    /Path=            FilePath to export the form template to as xsn]\r\n\t" +
                "\r\n\t" +
                "[/Account=           Only process the specified Account]\r\n\t" +
				"[/RequestKey=        Token used to communicate Groove host]\r\n\t" +
				"[/Host=              The remote Groove host name to issue commands to]\r\n\t" +
				"[/Timeout=           Timeout (in ms) for Web Services method execution]\r\n\t" +
				"[/X                  Configuration file to use for parameter input]\r\n\t" +
				"\r\n" +
				"NOTES:\r\n" +
                "The form in the ExamplePath is used to set the XML Processing Instructions that 'home' the form.\r\n" + 
                "This is necessary both to import a record into a Groove Form - the PI's must match that of the form\r\n" + 
                " and when using a form exported from a Groove Form - the PIs must match that of the target system";
        }

        public override CheckResult CheckParameters()
        {
            CommandUtils.UtilBase.CheckResult CheckResult = base.CheckParameters();
            if (CheckResult != CommandUtils.UtilBase.CheckResult.Succeeded)
                return CheckResult;

            mImport = base.GetParameterValueFlag("Import");
            mExport = base.GetParameterValueFlag("Export");
            mTemplate = base.GetParameterValueFlag("Template");

            if (mImport && !mExport && !mTemplate)
            {
                mFormURI = base.GetParameterValue("FormURI");
                mPath = base.GetParameterValue("Path");
                mExamplePath = base.GetParameterValue("ExamplePath");
                if (mFormURI == null || mPath == null)
                {
                    base.DisplayUsage("You must specify FormURI and Path for Import option");
                    return CommandUtils.UtilBase.CheckResult.Failed;
                }
            }
            else if (!mImport && mExport && !mTemplate)
            {
                mRecordURI = base.GetParameterValue("RecordURI");
                mFormURI = base.GetParameterValue("FormURI");
                mPath = base.GetParameterValue("Path");
                mExamplePath = base.GetParameterValue("ExamplePath");
                mExportAnyRecord = base.GetParameterValueFlag("ExportAnyRecord");
                if (((mRecordURI == null) && !mExportAnyRecord) || ((mFormURI == null) && mExportAnyRecord) || mPath == null)
                {
                    base.DisplayUsage("You must specify RecordURI (or FormURI and ExportAnyRecord) and Path for Export option");
                    return CommandUtils.UtilBase.CheckResult.Failed;
                }
            }
            else if (!mImport && !mExport && mTemplate)
            {
                mFormURI = base.GetParameterValue("FormURI");
                mPath = base.GetParameterValue("Path");
                if (mFormURI == null || mPath == null)
                {
                    base.DisplayUsage("You must specify FormURI and Path for Template option");
                    return CommandUtils.UtilBase.CheckResult.Failed;
                }
            }
            else
            {
                base.DisplayUsage("You must specify one and only one of the following mode options: /Import, /Export, /Template");
                return CommandUtils.UtilBase.CheckResult.Failed;
            }

            return CommandUtils.UtilBase.CheckResult.Succeeded;
        }

        public override CommandUtils.UtilBase.CheckResult Run()
        {
            if (mImport)
            {
                Import(mFormURI, mPath, mExamplePath);
            }
            else if (mExport)
            {
                if (!mExportAnyRecord)
                   Export(mRecordURI, mExportAnyRecord, mPath, mExamplePath);
                else
                    Export(mFormURI, mExportAnyRecord, mPath, mExamplePath);
            }
            else if (mTemplate)
            {
                Template(mFormURI, mPath);
            }

            return base.CheckUnusedParameters();
        }

        private void Import(String i_FormURI, String i_Path, String i_ExamplePath)
        {
            if (!File.Exists(i_Path))
            {
                throw new System.ArgumentException("Can't open file: " + i_Path);
            }

            if ((i_ExamplePath != null) && !File.Exists(i_ExamplePath))
            {
                throw new System.ArgumentException("Can't open file: " + i_ExamplePath);
            }

            // Open the space using a fixed up version of the FormURI
            string spaceURI = SpaceURIFromFormURI(i_FormURI);
            Space space = base.Identity.OpenSpace(spaceURI);
            if (space == null)
            {
                throw new System.ArgumentException("Can't open space for this tool, spaceURI=: " + spaceURI);
            }

            // Open the tool using a fixed up version of the FormURI
            string toolURI = ToolURIFromFormURI(i_FormURI);
            Tool tool = space.OpenTool(toolURI);
            if (tool.Type != GrooveWebServicesV12Helpers.Shared.GROOVE_INFOPATH_FORMS_TOOL_SUPPORTED_TYPE)
            {
                throw new System.ArgumentException("Invalid tool type: " + tool.Type);
            }

            // Open the form
            InfoPathForm ipForm = OpenForm(tool, i_FormURI);
            if (ipForm == null)
            {
                throw new System.ArgumentException("Invalid InfoPath Form URI: " + i_FormURI);
            }

            // read the InfoPath form from disk into an XMLDocument
            XmlDocument ipDoc = ReadIPForm(i_Path);

            // if an "example" form is given, use its InfoPathHostParameters instead of the source form's
            if (i_ExamplePath != null)
            {
                XmlDocument ipExampleDoc = ReadIPForm(i_ExamplePath);
                InfoPathHostParameters importParameters = InfoPathHostParameters.Instance(ipExampleDoc);
                importParameters.Fixup(ipDoc);
            }

            // create an instance of the record in the form
            string recordURI = ipForm.CreateInstance(ipDoc);

            base.StandardOutput.WriteLine("<GrooveInfoPathForms>");
            base.StandardOutput.WriteLine("    <Import URI='{0}'/>", recordURI);
            base.StandardOutput.WriteLine("</GrooveInfoPathForms>");
        }

        private void Export(String i_RecordURI, Boolean i_ExportAnyRecord, String i_Path, String i_ExamplePath)
        {
            if (File.Exists(i_Path))
            {
                throw new System.ArgumentException("Export file already exists: " + i_Path);
            }

            // Open the space using a fixed up version of the RecordURI
            string spaceURI = SpaceURIFromFormURI(i_RecordURI);
            Space space = base.Identity.OpenSpace(spaceURI);
            if (space == null)
            {
                throw new System.ArgumentException("Can't open space for this tool, spaceURI=: " + spaceURI);
            }

            // Open the tool using a fixed up version of the RecordURI
            string toolURI = ToolURIFromFormURI(i_RecordURI);
            Tool tool = space.OpenTool(toolURI);
            if (tool.Type != GrooveWebServicesV12Helpers.Shared.GROOVE_INFOPATH_FORMS_TOOL_SUPPORTED_TYPE)
            {
                throw new System.ArgumentException("Invalid tool type: " + tool.Type);
            }

            DataRow row = null;
            if (i_ExportAnyRecord)
            {
                GrooveWebServicesV12Helpers.Forms2.RecordDataSet rds = (tool as InfoPathFormsTool).GetRecords(false);
                if (rds != null)
                {
                    DataSet ds = rds.ToDataSet();
                    if (ds.Tables[0].Rows.Count > 0)
                    {
                        String uri = (String) ds.Tables[0].Rows[0]["RecordURI"];
                        row = ReadIPRecord(tool, uri);
                    }
                }
            }
            else
            {
                // Read the record
                row = ReadIPRecord(tool, i_RecordURI);
                if (row == null)
                {
                    throw new System.ArgumentException("Can't read record: " + i_RecordURI);
                }
            }

            XmlDocument IPFormInstance = InfoPathForm.ReadForm(row);

            // if an "example" form is given, use its InfoPathHostParameters instead of the source form's
            if (i_ExamplePath != null)
            {
                XmlDocument ipExampleDoc = ReadIPForm(i_ExamplePath);
                InfoPathHostParameters importParameters = InfoPathHostParameters.Instance(ipExampleDoc);
                importParameters.Fixup(IPFormInstance);
            }

            IPFormInstance.Save(i_Path);
        }

        private void Template(String i_FormURI, String i_Path)
        {
            if (File.Exists(i_Path))
            {
                throw new System.ArgumentException("Template file already exists: " + i_Path);
            }

            // Open the space using a fixed up version of the FormURI
            string spaceURI = SpaceURIFromFormURI(i_FormURI);
            Space space = base.Identity.OpenSpace(spaceURI);
            if (space == null)
            {
                throw new System.ArgumentException("Can't open space for this tool, spaceURI=: " + spaceURI);
            }

            // Open the tool using a fixed up version of the FormURI
            string toolURI = ToolURIFromFormURI(i_FormURI);
            Tool tool = space.OpenTool(toolURI);
            if (tool.Type != GrooveWebServicesV12Helpers.Shared.GROOVE_INFOPATH_FORMS_TOOL_SUPPORTED_TYPE)
            {
                throw new System.ArgumentException("Invalid tool type: " + tool.Type);
            }

            // open the form
            InfoPathForm ipForm = OpenForm(tool, i_FormURI);
            if (ipForm == null)
            {
                throw new System.ArgumentException("Invalid InfoPath Form URI: " + i_FormURI);
            }

            InfoPathFormsTool ipTool = ipForm.FormsTool;

            GrooveWebServicesV12Helpers.GrooveForms2WebService.InfoPathTemplate template = ipTool.ReadInfoPathTemplate(ipForm.URI);
            using (FileStream fs = new FileStream(i_Path, FileMode.Create, FileAccess.Write))
            {
                byte[] bytes = template.Contents.Item;
                fs.Write(bytes, 0, bytes.Length);
                fs.Close();
            }
        }

        private static XmlDocument ReadIPForm(String i_FormPath)
        {
            byte[] docAsBytes;
            using (FileStream fs = new FileStream(i_FormPath, FileMode.Open, FileAccess.Read))
            {
                docAsBytes = new byte[fs.Length];
                fs.Read(docAsBytes, 0, (int)fs.Length);
            }
            System.Text.Encoding enc = System.Text.Encoding.ASCII;
            string docAsString = enc.GetString(docAsBytes);
            docAsString = docAsString.TrimStart(new char[] { '?' });

            XmlDocument ipForm = new XmlDocument();
            ipForm.LoadXml(docAsString);
            return ipForm;
        }

        private static string SpaceURIFromFormURI(string i_URI)
        {
            string[] URIParts = i_URI.Split(new char[] { '/' });
            return "/GWS/Groove/2.0/Spaces/grooveTelespace/" + URIParts[6];
        }

        private static string ToolURIFromFormURI(string i_URI)
        {
            string[] URIParts = i_URI.Split(new char[] { '/' });
            return "/GWS/Groove/2.0/Tools/grooveTelespace/" + URIParts[6] + "/" + URIParts[7] + "/" + URIParts[8];
        }

        private static InfoPathForm OpenForm(Tool i_Tool, string i_FormURI)
        {
            InfoPathFormsTool ipfTool = (InfoPathFormsTool)i_Tool;

            InfoPathForm theForm = null;
            foreach (InfoPathForm form in ipfTool.Forms)
            {
                if (form.URI == i_FormURI)
                {
                    theForm = form;
                    break;
                }
            }
            return theForm;
        }

        private static DataRow ReadIPRecord(Tool i_Tool, string i_InstanceURI)
        {
            InfoPathFormsTool ipfTool = (InfoPathFormsTool)i_Tool;

            DataRow theRow = null;
            GrooveWebServicesV12Helpers.Forms2.RecordDataSet rds 
                = ipfTool.GetRecords(new string[] { i_InstanceURI }, true);
            if (rds != null)
            {
                theRow = rds.ToDataSet().Tables[0].Rows[0];
            }

            return theRow;
        }
    }

    public class InfoPathHostParameters
    {
        public String Href;

        public String SolutionVersion;

        public String Name;

        public InfoPathHostParameters()
        {
        }

        public InfoPathHostParameters(String i_Href, String i_SolutionVersion, String i_Name)
        {
            Href = i_Href;
            SolutionVersion = i_SolutionVersion;
            Name = i_Name;
        }

        static public InfoPathHostParameters Instance(XmlDocument i_XmlDoc)
        {
            InfoPathHostParameters ip = new InfoPathHostParameters();
            InfoPathFormsTool.GetPIAttributes(i_XmlDoc, out ip.Href, out ip.SolutionVersion, out ip.Name);
            return ip;
        }

        public void Fixup(XmlDocument i_XmlDoc)
        {
            InfoPathFormsTool.SetPIAttributes(i_XmlDoc, Href, SolutionVersion, Name);
        }
    }
}
